/******************************************************************************
 * Copyright (c) 2006 BEA Systems, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/
package org.gwt.project.tools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceRuleFactory;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;

public final class ToolsUtil {
	
	public static IFile[] toClasses(IFolder src) throws CoreException {
		List<IFile> files = new ArrayList<IFile>();
		for(IResource it : src.members()) {
			if(it instanceof IFile) {
				files.add((IFile)it);
			} else if(it instanceof IFolder) {
				IFile[] classes = toClasses((IFolder)it);
				files.addAll(Arrays.asList(classes));
			}
		}
		return files.toArray(new IFile[files.size()]);
	}

	public static List<IResource> getSrcLibs(IProject project, ToolsConfig config) throws CoreException {
		// Get folders
		IFolder src = project.getFolder(config.getSrcLib());
		// Get libs in folder
		List<IResource> libs = getLibs(src);		
		// Add paths to user source libs in referenced projects
		for(IProject it : project.getReferencedProjects()) {
			if(it.isAccessible()) {
				libs.addAll(getSrcLibs(it, config));
			}
		}
		// Finished
		return libs;
	}
	
	public static List<IResource> getDstLibs(IProject project, ToolsConfig config) throws CoreException {
		// Get folder
		IFolder dst = project.getFolder(config.getDstLib());
		// Get libs in folder
		List<IResource> libs = getLibs(dst);		
		// Add paths to user destination libs in referenced projects
		for(IProject it : project.getReferencedProjects()) {
			if(it.isAccessible()) {
				libs.addAll(getDstLibs(it, config));
			}
		}
		// Finished
		return libs;
	}
	
	public static List<IResource> getLibs(IFolder folder) throws CoreException {
		// Initialize
		List<IResource> paths = new ArrayList<IResource>();			
		// Folder exists?
		if(folder.exists()) {
			// Collect full paths
			for(IResource it : folder.members()) {
				if(isJar(it)) paths.add(it);
			}
		}		
		return paths;
	}
	
	public static boolean isJar(IResource resource) {
		return resource instanceof IFile && 
			   resource.getName().toLowerCase().endsWith(JAR);
	}
	
	public static List<IResource> copy(
			List<IResource> members, 
			IFolder dst,
			IProgressMonitor monitor) throws CoreException {
		
		// Has members?
		if (!members.isEmpty()) {
			
			// Initialize
			List<IResource> copied = new ArrayList<IResource>(members.size());
			
			// Ensure destination exists
			IPath path = ensure(dst, monitor).getFullPath();
			
			// Get workspace
			IWorkspace wp = dst.getWorkspace();
			
			// Loop over all members
			for(IResource it : members) {
				// Does not exist already?
				if(!dst.exists(new Path(it.getName()))) {
					wp.copy(new IResource[]{it}, path, true, monitor);
					copied.add(it);
				}
			}
			return copied;
		}
		return Collections.emptyList();
	}	
	
	public static List<IResource> delete(
			List<IResource> members, 
			IFolder dst,
			IProgressMonitor monitor) throws CoreException {
		
		// Has members?
		if (!members.isEmpty()) {
			
			// Initialize
			List<IResource> deleted = new ArrayList<IResource>(members.size());
			
			// Get workspace
			IWorkspace wp = dst.getWorkspace();
			
			// Loop over all members
			for(IResource it : members) {
				// Get library in destination folder
				it = dst.getFile(it.getName());
				// Does library exist?
				if(it.exists()) {
					wp.delete(new IResource[]{it}, true, monitor);
					deleted.add(it);
				}
			}
			return deleted;
		}
		return Collections.emptyList();
	}	

	public static final IFolder ensure(IFolder folder, IProgressMonitor monitor)
			throws CoreException {
		// Create destination folder?
		if (!folder.exists()) {
			// Create parent?
			if (!folder.getParent().exists()) {
				ensure((IFolder) folder.getParent(), monitor);
			}
			folder.create(true, true, monitor);
		}
		return folder;
	}

	
	/**
	 * Collect all projects referenced by this project.
	 * @param project
	 * @param set
	 * @return
	 * @throws CoreException
	 */
	public static IProject[] referenced(IProject project, 
			Set<IProject> set) throws CoreException {
		set.add(project);
		for(IProject it : project.getReferencedProjects()) {
			referenced(it,set);
		}
		return !set.isEmpty() ? 
				set.toArray(new IProject[set.size()]) :
					new IProject[0];
	}
	
	public static IProject[] referencing(IProject project) throws CoreException {
		return referencing(project, new HashSet<IProject>());
	}
	
	/**
	 * Collect all projects referencing this project.
	 * @param project
	 * @param set
	 * @return
	 * @throws CoreException
	 */
	public static IProject[] referencing(IProject project, 
			Set<IProject> set) throws CoreException {
		set.add(project);
		for(IProject it : project.getReferencedProjects()) {
			referenced(it,set);
		}
		return !set.isEmpty() ? 
				set.toArray(new IProject[set.size()]) :
					new IProject[0];
	}
	
	public static ISchedulingRule modifyAll(IProject project) throws CoreException {
		Set<IProject> union = new HashSet<IProject>();
		referenced(project, union);
		referencing(project, union);
		return modifyRule(union.toArray(new IProject[0]));
	}	
	
	
	public static ISchedulingRule modifyRule(IResource[] resources) {
		ISchedulingRule combinedRule = null;
		IResourceRuleFactory factory = ResourcesPlugin.
				getWorkspace().getRuleFactory();
		for (IResource it : resources) {
			ISchedulingRule rule = factory.modifyRule(it);
			combinedRule = MultiRule.combine(rule, combinedRule);
		}
		return combinedRule;
	}
	
	public static <T> void swap(T[] array, int i, int j) {
		T tmp = array[i];
		array[i] = array[j];
		array[j] = tmp;
	}
	
	public static <T> void move(T[] array, int i, int j) {
		for(int k=i; k<j; k++) {
			swap(array,k,j);
		}
	}

	private static final String JAR = ".jar";
	

}
